import React from 'react';
import { Input, Form, Col, Row, Button } from 'antd';
import classnames from 'classnames';

/**
 * 参数
 * columns 数据骨架Object
 * initialValues 表单初始值
 * layout 布局
 *   layout.gutter 每个col的间距
 *   layout.column 没行有几列
 * itemLayout 控制Item的lable和Input所占宽度
 * onFinish 结束
 * onFinishFailed 校验失败
 * onReset 重置回调
 * shouldChangUpdate 值改变后是否重新渲染
 * getFormRef 获取form引用
 * scrollToFirstError 滚动到第一个错误弹框
 * onSearch 表单提交时的回调
 * onReset 表单重置的回调
 * columns.title 显示的标题
 * columns.key 字段名称
 * columns.initialValue 默认值
 * columns.render 渲染组件，默认为Input组件
 *  *
 * xs 屏幕 < 576px
 * sm 屏幕 ≥ 576px
 * md 屏幕 ≥ 768px
 * lg 屏幕 ≥ 992px
 * xl 屏幕 ≥ 1200px
 * xxl 屏幕 ≥ 1600px
 */

const ItemLayout = {
  labelCol: {
    span: 8,
  },
  wrapperCol: {
    span: 16,
  },
};

interface IRule {
  required: boolean;
  message?: string;
  validator?: (...args: any[]) => Promise<any>;
  [k: string]: any;
}

interface ColumnProps {
  title: string; // Item的label
  key: string; // Item的name
  render?: (...args: any[]) => React.ReactNode;
  placeholder?: string;
  layout?: any;
  rules?: IRule[] | ((...args: any[]) => IRule[]);
  initialValue?: any;
  span?: number;
  isHidden?: (...args: any[]) => boolean; // 是否隐藏
}

export interface IProps {
  className?: string;
  columns: ColumnProps[] | any[];
  initialValues?: any; // 初始值 比initialValue优先级高
  submitBtnText?: string;
  resetBtnText?: string;
  buttonWrapStyle?: React.CSSProperties;
  hiddenResetBtn?: boolean;
  hiddenSubmitBtns?: boolean; // 隐藏所有提交按钮
  addonButton?: () => React.ReactNode;
  saveLoading?: boolean; // 保存按钮的loading
  onFinish: (...args: any[]) => void;
  onFinishFailed?: (...args: any[]) => void;
  onReset?: (...args: any[]) => void;
  itemLayout?: {
    // 控制Item
    labelCol: { span: number };
    wrapperCol: { span: number };
  };
  layout?: { gutter: number; column: number } | any;
  btnsAlign?: 'left' | 'center' | 'right';
  getFormRef?: (ref: React.RefObject<any>) => void;
  scrollToFirstError?: boolean;
  shouldChangUpdate?: boolean;
  showInModal?: boolean;
}

interface IState {
  initialFileds: any;
}

class FormPanel extends React.Component<IProps, IState> {
  static defaultProps = {
    columns: [],
    layout: { gutter: 15, column: 4 },
    itemLayout: ItemLayout,
    btnsAlign: 'right',
  };

  formRef: React.RefObject<any> = React.createRef();

  constructor(props: IProps) {
    super(props);
    // @ts-ignore
    const initialValues = (props.columns || []).reduce((memo: any, item: any) => {
      memo[item.key] = item.initialValue;
      return memo;
    }, {});

    this.state = {
      initialFileds: { ...initialValues, ...props.initialValues },
    };
  }

  componentDidMount() {
    const { getFormRef } = this.props;
    getFormRef && getFormRef(this.formRef);
  }

  createItem = ({
    title,
    render,
    key,
    placeholder,
    layout = {},
    span,
    rules,
    isHidden,
    ...rests
  }: ColumnProps) => {
    const { layout: itemLayout = { column: 4 } } = this.props;

    const fieldsValue = (this.formRef.current && this.formRef.current.getFieldsValue(true)) || {};

    if (typeof isHidden === 'function' && isHidden(fieldsValue)) {
      // 判断是否隐藏
      return null;
    }

    // render默认为input输入框
    if (!render) {
      render = () => <Input placeholder={placeholder || `请输入${title}`} />;
    }

    // 获取渲染得到的组件
    const comp = render(fieldsValue || {}, this.formRef.current);
    if (!comp) return null;

    const colSpan = span || 24 / itemLayout.column;

    if (!title && !key) {
      return <Col span={24} key={key}></Col>;
    }

    return (
      <Col span={colSpan} key={key}>
        <Form.Item
          {...layout}
          {...rests}
          label={title}
          name={key}
          className={`form-item__${key}`}
          key={key}
          rules={typeof rules === 'function' ? rules(fieldsValue) : rules}
        >
          {comp}
        </Form.Item>
      </Col>
    );
  };

  // 重置
  onReset = () => {
    this.formRef.current.resetFields();
    this.props.onReset && this.props.onReset();
  };

  handleSubmit = async (success?: (...args) => void) => {
    const { onFinish, onFinishFailed } = this.props;
    try {
      const values = await this.formRef.current.validateFields();
      onFinish && onFinish(values);
      success && success(values);
    } catch (error) {
      onFinishFailed && onFinishFailed(error);
    }
  };

  // 渲染底部按钮
  renderSubmit() {
    const {
      submitBtnText = '保存',
      resetBtnText = '重置',
      buttonWrapStyle = {},
      btnsAlign,
      hiddenResetBtn,
      addonButton,
      saveLoading,
      hiddenSubmitBtns,
      showInModal,
    } = this.props;
    if (hiddenSubmitBtns) return null;

    const styles = {
      textAlign: btnsAlign,
      ...buttonWrapStyle,
    };

    if (showInModal) {
      // 在modal中展示
      styles.margin = '0 -38px -28px -38px';
      styles.paddingTop = '10px';
      styles.borderTop = '1px solid #eee';
    }

    return (
      <Row gutter={24} className="com-search-panel__submit" style={styles}>
        <Col span={24}>
          {/* 挂载外部传入按钮 */}
          {addonButton && addonButton()}
          {!hiddenResetBtn && (
            <Button onClick={this.onReset} disabled={saveLoading}>
              {resetBtnText}
            </Button>
          )}
          <Button type="primary" loading={saveLoading} onClick={() => this.handleSubmit()}>
            {submitBtnText}
          </Button>
        </Col>
      </Row>
    );
  }

  onValuesChange = () => {
    const { shouldChangUpdate } = this.props;
    shouldChangUpdate && this.forceUpdate();
  };

  render() {
    const { initialFileds } = this.state;
    const { columns = [], className, layout, itemLayout } = this.props;

    return (
      <div className={classnames('com-form-panel', className)}>
        <Form
          ref={this.formRef}
          initialValues={initialFileds}
          {...itemLayout}
          onValuesChange={this.onValuesChange}
        >
          <Row gutter={layout?.gutter}>{columns.map(item => this.createItem(item))}</Row>
        </Form>
        {this.renderSubmit()}
      </div>
    );
  }
}

export default FormPanel;
